/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package seguridad;

import atributos.Atributo;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.swing.JOptionPane;

/**
 *
 * @author kastillo
 */
public class ManagerSeguridad  implements Constantes{
    private Seguridad seguridad = new Seguridad();
    public Seguridad getSeguridad() {
        return seguridad;
    }

    public void setSeguridad(Seguridad seguridad) {
        this.seguridad = seguridad;
    }
    
    public void traerClaves() {
         File file = new File(ARCH_SEGURIDAD);
            if (!file.exists()) {
                try {
                    file.createNewFile();
                    generarClaves();
                    guardarClaves();
                } catch (IOException ex) {
                    Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            else
            {
              cargarClaves();
            }
        }

    private void generarClaves() {
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM_ASIMETRICO);
            generator.initialize(1024);
            KeyPair keyPair = generator.generateKeyPair();

            seguridad.setLlavePublica(keyPair.getPublic());
            seguridad.setLlavePrivada(keyPair.getPrivate());

            KeyGenerator keygen = KeyGenerator.getInstance(ALGORITHM_SIMETRICO);
            SecretKey claveSecreta = keygen.generateKey();

            seguridad.setClaveSecreta(claveSecreta);

        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void cargarClaves() {

        cargarClavePrivada();
        cargarClavePublica();
  //      cargarClaveSecreta();
        
        FileInputStream fis = null;
        {
            ObjectInputStream ois = null;
            try {
                fis = new FileInputStream(ARCH_SEGURIDAD);
                ois = new ObjectInputStream(fis);
                while (fis.available() != 0) {
                    try {
                        seguridad = (Seguridad) ois.readObject();
                        System.out.println("otra" +seguridad.getLlavePrivada());
                    } catch (ClassNotFoundException ex) {
                        Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            } catch (IOException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            }
            finally {
                try {
                    fis.close();
                } catch (IOException ex) {
                    Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                }
                try {
                    ois.close();
                } catch (IOException ex) {
                    Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    private void guardarClaves() {

            guardarClavePublica();
            guardarClavePrivada();
            guardarClaveSecreta();
            
            ObjectOutputStream oos = null;
            try {
                FileOutputStream fos = new FileOutputStream(ARCH_SEGURIDAD);
                oos = new ObjectOutputStream(fos);
                oos.writeObject(seguridad);
            } catch (IOException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                try {
                    oos.close();
                } catch (IOException ex) {
                    Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        
    }

    public void cifrarArchivos(ArrayList<Atributo> propiedades) {

        for(int i=0;i<propiedades.size(); i++)
         {
            Atributo a = propiedades.get(i);
            String ruta = a.getIndex();
            String extension = a.getExt();
            if(!extension.equals("jxta"))
                    cifrar(ruta);
         }
    }

    public void descifrarArchivos(ArrayList<Atributo> propiedades) {
       for(int i=0;i<propiedades.size(); i++)
         {
            Atributo a = propiedades.get(i);
            String ruta = a.getIndex();
            String extension = a.getExt();
            if(extension.equals("jxta"))
                deCifrar(ruta);
         }
    }

    private void cifrar(String rutaCompleta) {
        try {
            Cipher cifrarCipher = Cipher.getInstance(INSTANCIA_SIMETRICO);
            // opteniendo la llave privada del programa
            SecretKey Key = seguridad.getClaveSecreta();
            // poniendolo en modo para encriptar
            cifrarCipher.init(Cipher.ENCRYPT_MODE, Key);
            //convertir a byte para cifrar
            byte[] archivoLimpio =  ConvertirArchivo.fileToByte(rutaCompleta);
            // cifrando el archivo
            byte[] archivoCifrado = cifrarCipher.doFinal(archivoLimpio);
            // actualizando el archivo cifrado en el directorio
            ConvertirArchivo.byteToFile(rutaCompleta+".jxta", archivoCifrado);
            ConvertirArchivo.borrarArchivo(rutaCompleta);

        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        }

            

    }

    private void deCifrar(String rutaCompleta) {
        try {
            Cipher deCifrarCipher = Cipher.getInstance(INSTANCIA_SIMETRICO);
            // opteniendo la llave privada del programa
            SecretKey Key = seguridad.getClaveSecreta();
            // poniendolo en modo para desencriptar
            deCifrarCipher.init(Cipher.DECRYPT_MODE, Key);
             //convertir a byte para descifrar
            byte[] archivoCifrado =  ConvertirArchivo.fileToByte(rutaCompleta);
            // se desencripta
            byte[] archivoDescifrado = deCifrarCipher.doFinal(archivoCifrado);

            String rutaSinExtension = ConvertirArchivo.quitarExtension(rutaCompleta);

           // actualizando el archivo descifrado en el directorio
            ConvertirArchivo.byteToFile(rutaSinExtension, archivoDescifrado);

            ConvertirArchivo.borrarArchivo(rutaCompleta);



        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private void guardarClavePublica() {
        FileOutputStream fos = null;
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            String fichero_publica;
            fichero_publica = ARCH_CLAVE_PUBLICA;
            fos = new FileOutputStream(fichero_publica);
            fos.write(seguridad.getLlavePublica().getEncoded());
            fos.close();
        } catch (IOException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fos.close();
            } catch (IOException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            }
        }


    }

    private void guardarClavePrivada() {

      Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

      FileOutputStream fos = null;
            try {

                SecureRandom sr = new SecureRandom();
                sr.setSeed(new Date().getTime());
                char[] password;
                password = JOptionPane.showInputDialog("Ingrese el password para guardar la llave privada:").toCharArray();
                byte[] salt = new byte[TAMANO_SALT_BYTES];
                sr.nextBytes(salt);
                PBEKeySpec clave_pbe = new PBEKeySpec(password);
                SecretKey clave_secreta_pbe = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC").generateSecret(clave_pbe);
                PBEParameterSpec pbe_param = new PBEParameterSpec(salt, ITERACIONES_PBE);
                Cipher cifrador_pbe = Cipher.getInstance("PBEWithSHAAndTwofish-CBC");
                cifrador_pbe.init(Cipher.ENCRYPT_MODE, clave_secreta_pbe, pbe_param);
                byte[] clave_privada_cifrada = cifrador_pbe.doFinal(seguridad.getLlavePrivada().getEncoded());
                fos = new FileOutputStream(ARCH_CLAVE_PRIVADA);
                fos.write(salt);
                fos.write(clave_privada_cifrada);
                fos.close();
                System.out.println("Fichero con clave privada generado");
            } catch (IOException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalBlockSizeException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (BadPaddingException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvalidKeyException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvalidAlgorithmParameterException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchPaddingException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchAlgorithmException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvalidKeySpecException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                try {
                    fos.close();
                } catch (IOException ex) {
                    Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

    private void cargarClavePrivada() {
        FileInputStream fis = null;
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            String password = JOptionPane.showInputDialog("Ingrese el password de la llave privada:");
            System.out.println("Recuperando clave privada...");
            SecureRandom sr = new SecureRandom();
            sr.setSeed(new Date().getTime());
            fis = new FileInputStream(ARCH_CLAVE_PRIVADA);
            byte[] buffer = new byte[TAMANO_SALT_BYTES];
            fis.read(buffer);
            PBEKeySpec clave_pbe_spec = new PBEKeySpec(password.toCharArray());
            SecretKey clave_pbe = SecretKeyFactory.getInstance("PBEWithSHAAndTwofish-CBC").generateSecret(clave_pbe_spec);
            PBEParameterSpec param_pbe_spec = new PBEParameterSpec(buffer, ITERACIONES_PBE);
            Cipher descifrador_pbe = Cipher.getInstance("PBEWithSHAAndTwofish-CBC");
            descifrador_pbe.init(Cipher.DECRYPT_MODE, clave_pbe, param_pbe_spec, sr);
            buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            buffer = descifrador_pbe.doFinal(buffer);
            PKCS8EncodedKeySpec clave_privada_spec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PrivateKey clave_privada = kf.generatePrivate(clave_privada_spec);

            seguridad.setLlavePrivada(clave_privada);
            System.out.println(""+ seguridad.getLlavePrivada());
            System.out.println("Clave secreta recuperada");
        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidAlgorithmParameterException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeySpecException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fis.close();
            } catch (IOException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    private void cargarClavePublica() {

        {

            try {
                PublicKey clave_publica = null;
                Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                System.out.println("Recuperando clave publica...");
                String fichero_publica = ARCH_CLAVE_PUBLICA;
                FileInputStream fis = new FileInputStream(fichero_publica);
                byte[] buffer = new byte[fis.available()];
                fis.read(buffer);
                fis.close();
                X509EncodedKeySpec clave_publica_spec = new X509EncodedKeySpec(buffer);
                KeyFactory kf = KeyFactory.getInstance("RSA");
                clave_publica = kf.generatePublic(clave_publica_spec);
                seguridad.setLlavePublica(clave_publica);
                System.out.println(""+ seguridad.getLlavePublica());
                System.out.println("clave publica recuperada");

                
            } catch (InvalidKeySpecException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchAlgorithmException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);

            } 
        }
    }


    private void guardarClaveSecreta() {


        FileOutputStream fos = null;
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            String fichero_secreta;
            fichero_secreta = ARCH_CLAVE_SECRETA;
            fos = new FileOutputStream(fichero_secreta);
            fos.write(seguridad.getClaveSecreta().getEncoded());
            fos.close();
        } catch (IOException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                fos.close();
            } catch (IOException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            }
        }


    }

    private void cargarClaveSecreta() {

     SecretKey claveSecreta= null;
     FileInputStream fis = null;
       
            ObjectInputStream ois = null;
            try {
                fis = new FileInputStream(ARCH_SEGURIDAD);
                ois = new ObjectInputStream(fis);
                while (fis.available() != 0) {
                    try {
                        claveSecreta = (SecretKey) ois.readObject();
                        System.out.println("clave secreta" +seguridad.getClaveSecreta());
                    } catch (ClassNotFoundException ex) {
                        Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            } catch (IOException ex) {
                Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
            }
            finally {
                try {
                    fis.close();
                } catch (IOException ex) {
                    Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                }
                try {
                    ois.close();
                } catch (IOException ex) {
                    Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }

    public void firmarTodosLosDocomentos(ArrayList<Atributo> propiedades) {
       for(int i=0;i<propiedades.size(); i++)
         {
            Atributo a = propiedades.get(i);
            String rutaCompleta = a.getIndex();
            String nombreArchivo = a.getNombreCompleto();
            firmarDocumento(rutaCompleta,nombreArchivo);
         }
    }

    private void firmarDocumento(String rutaCompleta,String nombreArchivo) {
       //Decifrando el archivo
        String ext = ConvertirArchivo.traerExtension(rutaCompleta);
       if(ext.equals("jxta")){
         deCifrar(rutaCompleta);
         rutaCompleta = ConvertirArchivo.quitarExtension(rutaCompleta);
         nombreArchivo = ConvertirArchivo.quitarExtension(nombreArchivo);
        }
        //Firmando el archivo
        firmar(rutaCompleta,nombreArchivo);
        // Cifrando el archivo
        cifrar(rutaCompleta);
    }

    private void firmar(String rutaCompleta,String nombreArchivo) {

        try {

            Signature s = Signature.getInstance(INSTANCIA_FIRMA);
            s.initSign(seguridad.getLlavePrivada());



            byte[] buffer = new byte[1024];
            buffer = ConvertirArchivo.fileToByte(rutaCompleta);

            s.update(buffer);

            buffer = s.sign();

            String archivoFirmado = nombreArchivo + ".sign";
            String rutaArchivoFirmado = RUTA_FIRMAS + archivoFirmado;

            ConvertirArchivo.byteToFile(rutaArchivoFirmado, buffer);
            System.out.println("Fichero firmado, la firma se deposito en " + rutaArchivoFirmado);

        } catch (SignatureException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(ManagerSeguridad.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    

}
